<?php

/**
 * @author      Nguyen Duc Thuan <me@ndthuan.com>
 * @copyright   Copyright (c) 2009 Nguyen Duc Thuan <me@ndthuan.com>
 * @version     $Id: Kernel.php 12 2009-11-03 03:17:46Z thuan.nguyen $
 */

final class Fete_Kernel
{
    /**
     *
     * @var Fete_Kernel
     */
    static protected $_instance = null;

    protected $_loadedAppInstances = array();

    protected $_appsDirectory = null;

    protected $_baseUrl = null;

    protected $_outputHandler = null;

    private $_isDispatched  = false;

    /**
     *
     * @var Fete_View
     */
    protected $_view = null;

    /**
     *
     * Get instance of this Fete class
     * @return Fete_Kernel
     */
    static public function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    /**
     *
     * @return Fete_View
     */
    public function getView()
    {
        if (null === $this->_view) {
            $this->_view = new Fete_View();
        }
        return $this->_view;
    }

    /**
     * @param bool $dispatched
     * @return Fete_Kernel
     */
    public function &setDispatched($dispatched)
    {
        $this->_isDispatched = $dispatched;

        return $this;
    }

    /**
     *
     * Check if this request is dispatched
     * @return bool
     */
    public function isDispatched()
    {
        return $this->_isDispatched;
    }

    public function run()
    {
        session_start();
        Fete_Request::getInstance()->init();

        ob_start($this->_outputHandler);
        while (false === $this->_isDispatched)
        {
            $req = Fete_Request::getInstance();
            $this->getAppInstance($req->getApp())
                ->run($req->getController(), $req->getAction());
        }
        ob_end_flush();
    }

    /**
     *
     * @param callback $callback
     * @return Fete_Kernel
     */
    public function &setOutputHandler($callback)
    {
        $this->_outputHandler = $callback;
        return $this;
    }

    /**
     *
     * @return callback
     */
    public function getOutputHandler()
    {
        return $this->_outputHandler;
    }

    /**
     * set applications directory
     * @param string the real path of apps dir
     * @return Fete_Kernel
     */
    public function &setAppsDirectory($directory)
    {
        $this->_appsDirectory = $directory;

        return $this;
    }

    /**
     *
     * @return string
     */
    public function getAppsDirectory()
    {
        return $this->_appsDirectory;
    }

    /**
     * @return Fete_Kernel
     */
    public function &setBaseUrl($baseUrl = null)
    {
        if (null === $baseUrl) {
            $this->_baseUrl = substr(
                $_SERVER['SCRIPT_NAME'],
                0,
                strrpos($_SERVER['SCRIPT_NAME'], '/')
            );
            return $this;
        }

        $this->_baseUrl = $baseUrl;

        return $this;
    }

    /**
     * Get base url of website
     * @return string
     */
    public function getBaseUrl()
    {
        if (null === $this->_baseUrl) {
            $this->setBaseUrl();
        }

        return $this->_baseUrl;
    }

    /**
     * @param $appName
     * @return Fete_Application
     */
    public function &getAppInstance($appName)
    {
        if (strpos($appName, '.') !== false || strpos($appName, '/') !== false
            || strpos($appName, '\\') !== false) {

            throw new Fete_Exception('Invalid application name');
        }

        if (isset($this->_loadedAppInstances[$appName])) {
            return $this->_loadedAppInstances[$appName];
        }

        $directory  = $this->_appsDirectory . DIRECTORY_SEPARATOR . $appName;

        if (file_exists($directory)) {
            $instance = new Fete_Application($appName, $directory);
            $this->_loadedAppInstances[$appName] = $instance;
            return $instance;
        }

        throw new Fete_Exception('Application ' . $appName . ' does not exist
            in ' . $this->_appsDirectory, 404);
        return false;
    }

    /**
     *
     * Disable construction and cloning...
     */
    protected function __construct()
    {}

    private function __clone() {}
}
